﻿using System;
using System.Collections.Generic;
using System.Linq;
using Bling.DSL;
using Bling.Util;
using Bling.Linq;
using Bling.Core;
using Bling.Matrices;
using Bling.Angles;
//using Bling.Vertices;

namespace Bling.Graphics {
  /// <summary>
  /// Basic texture interface.
  /// </summary>
  public interface ITexture {
    int DimCount { get; }
  }
  /// <summary>
  /// Basic texture root class.
  /// </summary>
  public abstract partial class Texture : ITexture {
    public abstract int DimCount { get; }
  }
  /// <summary>
  /// Basic texture root class.
  /// </summary>
  /// <typeparam name="DIM">Dimensions of texture; e.g., D2 for 2D texture.</typeparam>
  public abstract partial class Texture<DIM> : Texture where DIM : Dim1D<DIM> {
    public override int DimCount { get { return Dim1D<DIM>.Count; } }
  }
  /// <summary>
  /// Basic interface for image-based texture.
  /// </summary>
  public interface IImageTexture : ITexture {
    Uri URI { get; }
  }


  /// <summary>
  /// Image based texture defined via a URI.
  /// </summary>
  /// <typeparam name="DIM">Dimensions of texture.</typeparam>
  public partial class ImageTexture<DIM> : Texture<DIM>, IImageTexture where DIM : Dim1D<DIM> {
    public Uri URI { get; private set; }
    public ImageTexture(Uri URI) { this.URI = URI; }
    public override int GetHashCode() {
      return URI.GetHashCode();
    }
    public override bool Equals(object obj) {
      return obj is ImageTexture<DIM> && ((ImageTexture<DIM>)obj).URI.Equals(URI);
    }
  }
  public interface ISampleEval<EVAL> where EVAL : Eval<EVAL> {
    Eval<EVAL>.Info<Vecs.IVec<double,D4>> Sample<DIM, PT>(Expr<Texture<DIM>> argA, Expr<PT> argB) where DIM : Dim1D<DIM>;
  }

  public interface ISampleOp : Ops.IOperator {}
  public abstract partial class SampleOp<DIM, PT, CNT> : Ops.Operator<Texture<DIM>, PT, Vecs.IVec<double, D4>, CNT>, ISampleOp
    where DIM : Dim1D<DIM>
    where CNT : SampleOp<DIM, PT, CNT> { }

  /// <summary>
  /// Brand for textures.
  /// </summary>
  public abstract class TextureBl<DIM, PT, POINT, TEX, BRAND> : Brand<Texture<DIM>, BRAND>
    where POINT : Brand<PT, POINT>, IDouble<POINT>
    where TEX : BaseTex<POINT, TEX>
    where BRAND : TextureBl<DIM, PT, POINT, TEX, BRAND>
    where DIM : Dim1D<DIM> {
    public TextureBl(Expr<Texture<DIM>> Underlying) : base(Underlying) { }
    public TextureBl() : base() { }
    public override bool CanReallyTable {
      get { return false; }
    }

    public static implicit operator TEX(TextureBl<DIM, PT, POINT, TEX, BRAND> Texture) {
      if (Texture.Provider is ITableExpr<Texture<DIM>>) {
        // retable
        var e = (ITableExpr<Texture<DIM>>)Texture.Provider;
        var Values = new TEX[e.Values.Length];
        for (int i = 0; i < Values.Length; i++) {
          var ii = i;
          Values[ii] = BaseTex<POINT, TEX>.ToBrand(p => Op.Instance.Make(e.Values[ii], p.Provider));
        }
        return Values.Table(e.Index);
      }
      return BaseTex<POINT, TEX>.ToBrand(p => Op.Instance.Make(Texture.Provider, p.Provider));
    }

    /// <summary>
    /// Access texture according to default sample state.
    /// </summary>
    public ColorBl this[POINT p] { get { return ((TEX)this)[p]; } }

    internal partial class Op : SampleOp<DIM,PT,Op> {
      public new static readonly Op Instance = new Op();
      private Op() { }

      public override Expr<Vecs.IVec<double, D4>> Extent(Expr<Texture<DIM>> argA, Expr<PT> argB, bool Max) {
        Double4Bl p = Max ? 1d : 0d;
        return p.Provider;
      }
      public override SmartString Format(SmartString argA, SmartString argB) {
        return argA + ".Sample" + "(" + argB + ")";
      }
      public override Eval<EVAL>.Info<Vecs.IVec<double, D4>> Eval<EVAL>(Bling.Ops.Operation<Texture<DIM>, PT, Vecs.IVec<double, D4>> op, Eval<EVAL> txt) {
        if (txt is ISampleEval<EVAL>)
          return ((ISampleEval<EVAL>)txt).Sample<DIM, PT>(op.ArgA, op.ArgB);
        return base.Eval<EVAL>(op, txt);
      }
    }
  }
  /// <summary>
  /// Brand for 2D textures.
  /// </summary>
  public class Texture2Bl : TextureBl<D2, Vecs.IVec<double, D2>, Double2Bl, Tex2D, Texture2Bl> {
    public Texture2Bl(Expr<Texture<D2>> Underlying) : base(Underlying) { }
    public Texture2Bl() : base() { }
    public static implicit operator Texture2Bl(Expr<Texture<D2>> e) { return new Texture2Bl(e); }
    public static implicit operator Texture2Bl(Texture<D2> t) { return new Constant<Texture<D2>>(t); }
  }
  /// <summary>
  /// Brand for 1D textures.
  /// </summary>
  public class Texture1Bl : TextureBl<D1, double, DoubleBl, Tex1D, Texture1Bl> {
    public Texture1Bl(Expr<Texture<D1>> Underlying) : base(Underlying) { }
    public Texture1Bl() : base() { }
    public static implicit operator Texture1Bl(Expr<Texture<D1>> e) { return new Texture1Bl(e); }
    public static implicit operator Texture1Bl(Texture<D1> t) { return new Constant<Texture<D1>>(t); }
  }
  /// <summary>
  /// Brand for 3D textures.
  /// </summary>
  public class Texture3Bl : TextureBl<D3, Vecs.IVec<double, D3>, Double3Bl, Tex3D, Texture3Bl> {
    public Texture3Bl(Expr<Texture<D3>> Underlying) : base(Underlying) { }
    public Texture3Bl() : base() { }
    public static implicit operator Texture3Bl(Expr<Texture<D3>> e) { return new Texture3Bl(e); }
    public static implicit operator Texture3Bl(Texture<D3> t) { return new Constant<Texture<D3>>(t); }
  }



  public interface ITex<POINT> where POINT : Brand<POINT>, IDouble<POINT> {
    ColorBl this[POINT p] { get; }
  }
  public static class BaseTex<POINT> where POINT : Brand<POINT>, IDouble<POINT> {
    public static Func<Func<POINT, ColorBl>, ITex<POINT>> Make { get; internal set; }
    static BaseTex() {
      new Tex1D((DoubleBl d) => new ColorBl(1, 1, 1, 1));
      new Tex2D((Double2Bl d) => new ColorBl(1, 1, 1, 1));
      new Tex3D((Double3Bl d) => new ColorBl(1, 1, 1, 1));
    }
  }

  public abstract partial class BaseTex<POINT, BRAND> : ArgDoubleFuncBl<POINT, ColorBl, BRAND>, ITex<POINT>
    where POINT : Brand<POINT>, IDouble<POINT>
    where BRAND : BaseTex<POINT, BRAND> {
    public BaseTex(Func<POINT, ColorBl> F) : base(F) { }
    public BaseTex(Func<Func<BRAND, ColorBl>, ColorBl> F) : base(F) { }
    static BaseTex() {
      BaseTex<POINT>.Make = f => ToBrand(f);
      new Tex1D((DoubleBl d) => new ColorBl(1, 1, 1, 1));
      new Tex2D((Double2Bl d) => new ColorBl(1, 1, 1, 1));
      new Tex3D((Double3Bl d) => new ColorBl(1, 1, 1, 1));
    }
    public new static Func<Func<POINT,ColorBl>, BRAND> ToBrand { get { return ArgDoubleFuncBl<POINT, ColorBl, BRAND>.ToBrand; } }

    /// <summary>
    /// Distort texture by transforming its input point.
    /// </summary>
    public BRAND Distort(Func<POINT, POINT> G) {
      return ToBrand(p => this[G(p)]);
    }
    /// <summary>
    /// Access color at texture coordinate p.
    /// </summary>
    /// <param name="p">Texture coordinate.</param>
    /// <returns>Color at specified texture coordinate.</returns>
    public new ColorBl this[POINT p] { get { return base[p]; } }

    /// <summary>
    /// Shift the left top corner of the texture's view box.
    /// </summary>
    public BRAND Offset(POINT d) {
      return ToBrand(p => this[d.Add(p)]);
    }
  }
  public partial class Tex1D : BaseTex<DoubleBl, Tex1D> {
    /// <summary>
    /// Create a new texture with specified function.
    /// </summary>
    /// <param name="F">Function from a texture coordinate ([0,0]-[1,1]) to a color.</param>
    public Tex1D(Func<DoubleBl, ColorBl> F) : base(F) { }
    public Tex1D(Func<Func<Tex1D, ColorBl>, ColorBl> F) : base(F) { }

    /// <summary>
    /// Texture flipped.
    /// </summary>
    public Tex1D Flip { get { return Distort(p => 1d - p); } }


    public static implicit operator Tex1D(Func<DoubleBl, ColorBl> F) { return new Tex1D(F); }
    /// <summary>
    /// Create a solid color texture.
    /// </summary>
    /// <param name="Color">Color of texture at every point.</param>
    public static explicit operator Tex1D(ColorBl Color) { return new Tex1D((DoubleBl p) => Color); }
  }


  /// <summary>
  /// A 2 dimensional texture defined as a function from texture coordinates ([0,0]-[1,1]) to colors.
  /// </summary>
  public partial class Tex2D : BaseTex<Double2Bl, Tex2D> {
    /// <summary>
    /// Create a new texture with specified function.
    /// </summary>
    /// <param name="F">Function from a texture coordinate ([0,0]-[1,1]) to a color.</param>
    public Tex2D(Func<Double2Bl, ColorBl> F) : base(F) { }
    public Tex2D(Func<Func<Tex2D, ColorBl>, ColorBl> F) : base(F) { }
    /// <summary>
    /// Texture flipped horizontally.
    /// </summary>
    public Tex2D FlipHorizontal { get { return Distort(p => new Double2Bl(1d - p.X, p.Y)); } }
    /// <summary>
    /// Texture flipped vertically.
    /// </summary>
    public Tex2D FlipVertical { get { return Distort(p => new Double2Bl(p.X, 1d - p.Y)); } }

    /// <summary>
    /// Crop the viewbox of the texture.
    /// </summary>
    public Tex2D Crop(Double2Bl LeftTop, Double2Bl Size) {
      return new Tex2D(p => this[LeftTop + p.Clamp(new Double2Bl(0,0), Size)]);
    }
    public static implicit operator Tex2D(Func<Double2Bl, ColorBl> F) { return new Tex2D(F); }
    /// <summary>
    /// Create a solid color texture.
    /// </summary>
    /// <param name="Color">Color of texture at every point.</param>
    public static explicit operator Tex2D(ColorBl Color) { return new Tex2D((Double2Bl p) => Color); }

    /// <summary>
    /// Access color at texture coordinate (x,y).
    /// </summary>
    public ColorBl this[DoubleBl x, DoubleBl y] { get { return this[new Double2Bl(x, y)]; } }

    /// <summary>
    /// Create a height field from the red channel of a texture.
    /// </summary>
    public static explicit operator HeightField(Tex2D Tex) {
      return new HeightField(p => { return Tex[p].ScR; }); // use the red channel.
    }
    /// <summary>
    /// Create a normal map from the RGB channel of a texture. 
    /// </summary>
    public static explicit operator NormalMap(Tex2D Tex) {
      return new NormalMap(UV => {
        return (Double3Bl)(Tex[UV].ScRGB * 2d - 1d);
      });
    }
  }
  public partial class Tex3D : BaseTex<Double3Bl, Tex3D> {
    /// <summary>
    /// Create a new texture with specified function.
    /// </summary>
    /// <param name="F">Function from a texture coordinate ([0,0]-[1,1]) to a color.</param>
    public Tex3D(Func<Double3Bl, ColorBl> F) : base(F) { }
    public Tex3D(Func<Func<Tex3D, ColorBl>, ColorBl> F) : base(F) { }


    public static implicit operator Tex3D(Func<Double3Bl, ColorBl> F) { return new Tex3D(F); }
    /// <summary>
    /// Create a solid color texture.
    /// </summary>
    /// <param name="Color">Color of texture at every point.</param>
    public static explicit operator Tex3D(ColorBl Color) { return new Tex3D((Double3Bl p) => Color); }
  }

  /// <summary>
  /// A 2D pixel effect defined as a function from/to 2D textures.
  /// </summary>
  public partial class PixelEffect : ArgDoubleFuncBl<Tex2D, Tex2D, PixelEffect> {
    public PixelEffect(Func<Tex2D, Tex2D> F) : base(F) { }
    public PixelEffect(Func<Tex2D, Double2Bl, ColorBl> F) : this(input => new Tex2D(uv => F(input, uv))) { }
    public PixelEffect(Func<Tex2D, Func<Double2Bl, ColorBl>> F) : this(input => new Tex2D(uv => F(input)(uv))) { }
    private PixelEffect(Func<Func<PixelEffect, Tex2D>, Tex2D> F) : base(F) { }
    public static PixelEffect MakeFunc(Func<Func<PixelEffect, Tex2D>, Tex2D> F) {
      return new PixelEffect(F);
    }

    public static implicit operator Func<Tex2D, Func<Double2Bl, ColorBl>>(PixelEffect pixel) {
      return tex => pixel.Function(tex);
    }
    public static implicit operator Func<Tex2D, Double2Bl, ColorBl>(PixelEffect pixel) {
      return (tex,uv) => pixel[tex][uv];
    }
    public static implicit operator PixelEffect(Func<Tex2D, Tex2D> F) { return new PixelEffect(F); }
    public static implicit operator PixelEffect(Func<Tex2D, Func<Double2Bl, ColorBl>> F) { return new PixelEffect(F); }
    public static implicit operator PixelEffect(Func<Tex2D, Double2Bl, ColorBl> F) { return new PixelEffect(F); }

    // used only for Id purposes. 
    private class FakeTexOp : Ops.Operator<Vecs.IVec<double,D2>, Vecs.IVec<double,D4>, FakeTexOp> {
      public static new readonly FakeTexOp Instance = new FakeTexOp();
      private FakeTexOp() { }
      public override SmartString Format(SmartString s) {
        return "ArgTex[" + s + "]";
      }
    }
    /// <summary>
    /// Compose pixel effects (circle composition).
    /// </summary>
    public PixelEffect Apply(PixelEffect Other) {
      return ToBrand(a => this[Other[a]]);  
    }
  }
  /// <summary>
  /// A transition effect to a specified "next" texture, defined as a function (Progress, RandomSeed, NextTexture) => PixelEffect. 
  /// </summary>
  public partial class Transition : DoubleFuncBl<DoubleBl,DoubleBl,Tex2D,PixelEffect,Transition> {
    /// <summary>
    /// Create a new transition defined by the specified function.
    /// </summary>
    /// <param name="F">A function from progress [0,1], random seed [0,1], and a specified next texture, to a pixel effect that implements the transition (according to progress).</param>
    public Transition(Func<DoubleBl, DoubleBl, Tex2D, PixelEffect> F) : base(F) { }
    public Transition(Func<Func<Transition,PixelEffect>,PixelEffect> F) : base(F) { }
    public static implicit operator Transition(Func<DoubleBl, DoubleBl, Tex2D, PixelEffect> F) { return new Transition(F); }

    /// <summary>
    /// Apply transition function to create a pixel effect.
    /// </summary>
    public new PixelEffect this[DoubleBl Progress, DoubleBl RandomSeed, Tex2D NextInput] {
      get { return Function(Progress, RandomSeed, NextInput); }
    }
    /// <summary>
    /// Compose transitions.
    /// </summary>
    public Transition Apply(Transition Other) {
      return ToBrand((a, b, c) => this[a, b, c].Apply(Other[a, b, c])); 
    }
  }


  public partial interface ICurve {

  }
  /// <summary>
  /// A parametric cruve defined by a function F from a value between (0,1) to a point from [0,0] to [1,1].
  /// </summary>
  public class PCurve : ArgDoubleFuncBl<DoubleBl,Double2Bl,PCurve>, ICurve {
    public PCurve(Func<DoubleBl, Double2Bl> F) : base(F) { }
    private PCurve(Func<Func<PCurve, Double2Bl>, Double2Bl> F) : base(F) { }

    public static PCurve MakeFunc(Func<Func<PCurve, Double2Bl>, Double2Bl> F) { return new PCurve(F); }

    public static implicit operator PCurve(Func<DoubleBl,Double2Bl> F) { return new PCurve(F); }

    /// <summary>
    /// Normals for this curve, which is a vector field perpendicular to this curve.
    /// </summary>
    public Func<DoubleBl, Double2Bl> Normals {
      get {
        var Df = this.Function.Derivative<Double2Bl,D2>();
        if (Df == null)
          throw new NotSupportedException();
        return t => {
          var p = Df(t);
          return p.ClockwisePerp.Normalize;
        };

      }
    }
  }
  public partial interface ICurve3D {

  }
  /// <summary>
  /// 3D parametric curve.
  /// </summary>
  public class PCurve3D : ArgDoubleFuncBl<DoubleBl,Double3Bl,PCurve3D>, ICurve3D {
    public PCurve3D(Func<DoubleBl, Double3Bl> F) : base(F) {}
    /// <summary>
    /// Sweep this curve with another to form a surface.
    /// </summary>
    public Surface Sweep(Func<DoubleBl, Double3Bl> Scurve) {
      return new Surface(p => this[p.X] + Scurve(p.Y));
    }
    public static implicit operator PCurve3D(Func<DoubleBl, Double3Bl> F) {
      return new PCurve3D(F);
    }
    public static implicit operator PCurve3D(PCurve Curve) {
      return Curve.Function;
    }
    public static implicit operator PCurve3D(Func<DoubleBl, Double2Bl> F) {
      return new PCurve3D(t => new Double3Bl(F(t), 0d));
    }
    /// <summary>
    /// Slope of this curve.
    /// </summary>
    public Func<DoubleBl, Double3Bl> Slope {
      get {
        var Df = this.Function.Derivative<Double3Bl, D3>();
        if (Df == null)
          throw new NotSupportedException();
        return t => {
          var p = Df(t);
          return p.Normalize;
        };

      }
    }
    /// <summary>
    /// Normals of this curve with respect to a specified axis.
    /// </summary>
    public Func<DoubleBl, Double3Bl> Normals(Double3Bl Axis) {
      return d => Slope(d).Cross(Axis);
    }
  }

  public partial interface IMesh3DEntry {
    IntBl Index { get; }
    Double3Bl Position { get; }
    Double3Bl Normal { get; }
    Double2Bl Texture { get; }
  }

  public partial interface IMesh3D {
    IntBl VertexCount { get; }
    IntBl PrimitiveCount { get; }
    IMesh3DEntry this[IntBl Index] { get; }
    Func<IntBl,IntBl<D3>> Indices { get; }
    bool HasNormals { get; }
    bool HasTextures { get; }
  }


  public partial interface IMesh3D<ENTRY> : IMesh3D where ENTRY : IMesh3DEntry {
    new ENTRY this[IntBl Index] { get; }
  }
  public abstract class Mesh3D<SELF> where SELF : Mesh3D<SELF> {
    public class Mesh3DEntry : IMesh3DEntry {
      internal protected SELF Outer { get; set; }
      public IntBl Index { get; internal set; }
      public Double3Bl Position { get { return Outer.Positions(Index); } }
      public Double3Bl Normal { get { return Outer.Normals(Index); } }
      public Double2Bl Texture { get { return Outer.Textures(Index); } }
    }
    protected Func<IntBl, Double3Bl> Positions { private get; set; }
    protected Func<IntBl, Double3Bl> Normals { private get; set; }
    protected Func<IntBl, Double2Bl> Textures { private get; set; }
    public IntBl VertexCount { get; protected set; }
    public IntBl PrimitiveCount { get; protected set; }
    public Func<IntBl, IntBl<D3>> Indices { get; protected set; }
    public bool HasNormals { get { return Normals != null; } }
    public bool HasTextures { get { return Textures != null; } }
  }
  public abstract class Mesh3D<SELF,ENTRY> : Mesh3D<SELF>, IMesh3D<ENTRY> where SELF : Mesh3D<SELF,ENTRY> where ENTRY : Mesh3D<SELF>.Mesh3DEntry, new() {
    public ENTRY this[IntBl Index] { get { return new ENTRY() { Index = Index, Outer = (SELF) this }; } }
    IMesh3DEntry IMesh3D.this[IntBl Index] { get { return this[Index]; } }
  }

  public class FaceMesh : Mesh3D<FaceMesh,Mesh3D<FaceMesh>.Mesh3DEntry> {
    private static readonly Func<IntBl, Double2Bl> VerticesF = (i) => new Double2Bl((i / 2) * 2 - 1, (((i + 1) % 4) / 2) * 2 - 1);
    public FaceMesh() {
      this.VertexCount = 4;
      this.PrimitiveCount = 2;
      this.Normals = (i) => new Double3Bl(0, 0, -1).VerifyNormalized;

      this.Positions = (i) => VerticesF(i).AddZ(0);
      this.Textures = (i) => (VerticesF(i) + 1) / 2;
      this.Indices = (i) => new IntBl<D3>(0, 1 + i, 2 + i);
    }
  }
  /// <summary>
  /// A basic cube topology for use in 3D geometry computations. 
  /// </summary>
  public class CubeMesh : Mesh3D<CubeMesh,CubeMesh.Mesh3DEntry> {
    //public int Count { get { return 4 * 6; } }
    private static readonly FaceMesh Face = new FaceMesh();
    public CubeMesh() {
      this.VertexCount = Face.VertexCount * 6;
      this.PrimitiveCount = Face.PrimitiveCount * 6;
      // which primitive are we? and what is our base vertex
      this.Indices = (i) => Face.Indices(i % Face.PrimitiveCount) + (i / Face.PrimitiveCount) * Face.VertexCount;
      this.Textures = (i) => Face[i % Face.VertexCount].Texture;


      // 0: 0, 1, 0 0.0PI
      // 1: 1, 0, 0 0.5PI
      // 2: 0, 1, 0 0.5PI

      // 3: 1, 0, 0 1.0PI
      // 4: 0, 1, 0 1.5PI
      // 5: 1, 0, 0 1.5PI

      Func<IntBl,QuaternionBl> Rotate = i =>
        new QuaternionBl(new Double3Bl(0+i%2,1-i%2,0), (((DoubleBl) (i/3*2+(i%3).Min(1))) * .5).PI());

      this.Normals = (i) => Rotate(i / Face.VertexCount).Transform(Face[i % Face.VertexCount].Normal);
      this.Positions = (i) => Rotate(i / Face.VertexCount).Transform(Face[i % Face.VertexCount].Position.XY().AddZ(-1));
    }

    public new class Mesh3DEntry : Mesh3D<CubeMesh>.Mesh3DEntry {
      public IntBl Face { get { return Index / 4; } }
    }
    public class CubeFaceMesh : Mesh3D<CubeFaceMesh, Mesh3D<CubeFaceMesh>.Mesh3DEntry> {
      internal CubeFaceMesh(CubeMesh Outer, int FaceId) {
        VertexCount = Outer.VertexCount / 6;
        PrimitiveCount = Outer.PrimitiveCount / 6;
        this.Positions = (i) => Outer[FaceId * VertexCount + i].Position;
        if (Outer.HasNormals)
          this.Normals = (i) => Outer[FaceId * VertexCount + i].Normal;
        if (Outer.HasTextures)
          this.Textures = (i) => Outer[FaceId * VertexCount + i].Texture;

        Indices = (i) => Outer.Indices(FaceId * PrimitiveCount + i) - FaceId * VertexCount;
      }
    }
    public CubeFaceMesh GetFace(int FaceId) {
      return new CubeFaceMesh(this, FaceId);
    }
  }
  /// <summary>
  /// Mesh entry with some kind of key, supports re-keying.
  /// </summary>
  public interface IKeyMesh3DEntry<SELF,KEY> : IMesh3DEntry where SELF : IKeyMesh3DEntry<SELF,KEY> where KEY:Brand<KEY> {
    /// <summary>
    /// Key of entry
    /// </summary>
    KEY Key { get; }
    /// <summary>
    /// Re-key entry.
    /// </summary>
    SELF Set(KEY Key);
  }

  /// <summary>
  /// A parametric surface formed by a function F from +-[1/2,1/2] to +-[1,1,1].
  /// </summary>
  public class Surface : ArgDoubleFuncBl<Double2Bl,Double3Bl,Surface> {
    /// <summary>
    /// Create mesh with specified 2D width and height.
    /// </summary>
    public IMesh3D<Entry> this[int Width, int Height] {
      get { return new Mesh(this, Width, Height); }
    }
    public interface Entry : IKeyMesh3DEntry<Entry,Double2Bl> {
      /// <summary>
      /// Computed tangent of this mesh entry.
      /// </summary>
      Double3Bl Tangent { get; }
    }
    public static Double2Bl IndexToKey(int Width, int Height, IntBl Index) {
      return new Double2Bl(Index % Width, Index / Width) / new Double2Bl(Width - 1, Height - 1) - .5d;
    }


    private class Mesh : IMesh3D<Entry> {
      private readonly Surface Outer;
      public readonly int Width;
      public readonly int Height;
      public IntBl VertexCount { get { return Width * Height; } }
      public IntBl PrimitiveCount { get { return ((Width-1) * (Height-1)) * 2; } }

      internal Mesh(Surface Outer, int Width, int Height) {
        this.Width = Width; this.Height = Height;
        this.Outer = Outer; 
      }
      private class Entry0 : Entry {
        internal Mesh Outer;
        public Double2Bl Key { get; internal set; }
        public IntBl Index { get; internal set; }
        public Double2Bl Texture {
          get { return Key + .5d; } 
        }
        public Double3Bl Normal {
          get { return Outer.Outer.Normals[Key]; }
        }
        public Double3Bl Tangent {
          get { return Outer.Outer.Tangents[Key]; }
        }
        public Double3Bl Position {
          get { return Outer.Outer[Key]; }
        }
        public Entry Set(Double2Bl Key) {
          return new Entry0() { Outer = Outer, Index = Index, Key = Key };
        }
      }
      public Surface.Entry this[IntBl Index] { get { return new Entry0() { Outer = this, Index = Index, Key = IndexToKey(Width, Height, Index) }; } }
      IMesh3DEntry IMesh3D.this[IntBl Index] { get { return this[Index]; } }
      public Func<IntBl,IntBl<D3>> Indices {
        get {
          // Idx = (Width - 1) * (Height - 1) * 2
          Func<IntBl, IntBl, IntBl> fN = (i, j) => (j % Height) * Width + (i % Width);
          return Idx => {
            // find out our triangle position.
            // go from left to right.
            var I = (Idx / 2) % (Width-1);
            var J = (Idx / 2) / (Width-1);
            var v00 = fN(I + 0, J + 0);
            var v11 = fN(I + 1, J + 1);
            var v01 = fN(I + 0, J + 1);
            var v10 = fN(I + 1, J + 0);

            var vA = new IntBl<D3>(v00, v01, v10);
            var vB = new IntBl<D3>(v11, v10, v01);
            return (Idx % 2 == 0).Condition(vA, vB);

            // we are triangle I, J

            //var v0 = (J + 1        ) * Width + I + 0;
            //var v1 = (J + 0        ) * Width + I + (Idx % 2);
            //var v2 = (J + (Idx % 2)) * Width + I + 1;
            //return new IntBl<D3>(v0, v1, v2);
            // find base vertex.
            //  0 1 2 3
            //  *-*-*-*
            //  |/|/|/|
            //  *-*-*-*
            //  4 5 6 7
          };

          // two triangles.
          /*
          var Indices = new List<int>();
          for (int i = 0; i < Width - 1; i++) {
            for (int j = 0; j < Height - 1; j++) {
              Indices.Add(fN(i + 0, j + 0), fN(i + 0, j + 1), fN(i + 1, j + 0));
              Indices.Add(fN(i + 1, j + 1), fN(i + 1, j + 0), fN(i + 0, j + 1));
            }
          }
          return Indices;
           */
        }
      }
      public bool HasTextures { get { return true; } }
      public bool HasNormals { get { return Outer.HasDerivative; } }
      public bool HasTangents { get { return Outer.HasDerivative; } }
    }



    /// <summary>
    /// Construct a parametric surface using F.
    /// </summary>
    /// <param name="F">A function from +-[1/2,1/2] to +-[1,1,1], which forms a 3D position.</param>
    public Surface(Func<Double2Bl, Double3Bl> F) : base(F) { }

    private Surface(Func<Func<Surface, Double3Bl>, Double3Bl> F) : base(F) { }
    public static Surface MakeFunc(Func<Func<Surface, Double3Bl>, Double3Bl> F) {
      return new Surface(F);
    }

    public static implicit operator Surface(Func<Double2Bl, Double3Bl> F) {
      return new Surface(F);
    }
    public static Surface operator *(Matrix4Bl Matrix, Surface Surface) {
      return new Surface(p => Matrix.Transform(Surface[p]));
    }

    /// <summary>
    /// Displace this surface using a height field to create a new surface.
    /// </summary>
    /// <param name="HeightField">Height field that will displace surface.</param>
    /// <returns>Displaced surface.</returns>
    public Surface Displace(Func<Double2Bl, DoubleBl> HeightField) {
      return new Surface(p =>
        this[p] + Normals[p] * HeightField(p));
    }
    private Func<Double2Bl, MatrixBl<D3, D2>> Derivative0;

    public bool HasDerivative {
      get {
        if (Derivative0 != null) return true;
        Derivative0 = this.Derivative();
        return Derivative0 != null;
      }
    }

    /// <summary>
    /// The normal mapping for this surface computed from the partial derivatives of parametric position function F.
    /// </summary>
    /// <exception cref="NotSupportedException">If F's derivatives cannot be computed automatically.</exception>
    public NormalMap Normals {
      // we can use derivation.
      get {
        if (Normals0 == null) {
          if (!HasDerivative) throw new NotSupportedException("no normal map");
          Normals0 = new NormalMap(p => {
            var M = Derivative0(p); // oh...so nice!
            var rowA = M.Columns[0];
            var rowB = M.Columns[1];

            var cross = ((Double3Bl)rowA).Cross((Double3Bl)rowB);
            return cross.Normalize;
          });
        }
        return Normals0;
      } 
    }


    private NormalMap Normals0 = null;
    private NormalMap Tangents0 = null;

    public NormalMap Tangents {
      // we can use derivation.
      get {
        if (Tangents0 == null) {
          if (!HasDerivative) throw new NotSupportedException("no tangent map");
          Tangents0 = new NormalMap(p => {
            return ((Double3Bl)Derivative0(p).Columns[0]).Normalize;
          });
        }
        return Tangents0;
      } 
    }


  }
  /// <summary>
  /// A specification of 3D normals for an otherwise 2D surface.
  /// </summary>
  public class NormalMap : ArgDoubleFuncBl<Double2Bl,Double3Bl,NormalMap> {
    public NormalMap(Func<Double2Bl, Double3Bl> F) : base(F) { }
    private NormalMap(Func<Func<NormalMap,Double3Bl>, Double3Bl> F) : base(F) { }
    public static NormalMap MakeFunc(Func<Func<NormalMap, Double3Bl>, Double3Bl> F) {
      return new NormalMap(F);
    }

    public static implicit operator NormalMap(Func<Double2Bl, Double3Bl> F) { return new NormalMap(F); }
    public static implicit operator Surface(NormalMap F) { return F.Function; }
  }
  /// <summary>
  /// A specification of heights for a 2D surface. 
  /// </summary>
  public class HeightField : ArgDoubleFuncBl<Double2Bl,DoubleBl,HeightField> {
    public HeightField(Func<Double2Bl, DoubleBl> F) : base(F) { }
    private HeightField(Func<Func<HeightField, DoubleBl>, DoubleBl> F) : base(F) { }
    public static HeightField MakeFunc(Func<Func<HeightField, DoubleBl>, DoubleBl> F) {
      return new HeightField(F);
    }
    public static implicit operator HeightField(Func<Double2Bl, DoubleBl> F) { return new HeightField(F); }
    public static implicit operator Surface(HeightField F) {
      return new Surface(p => new Double3Bl(p * 2d, F[p]));
    }
    public static implicit operator Func<Double2Bl, Double3Bl>(HeightField F) {
      return ((Surface)F);
    }
    /// <summary>
    /// Create new height field with adjusted frequency, magnitude, and phase.
    /// </summary>
    /// <param name="Frequency">The number of occurrences of a repeating distortion per unit length. Suggested values: 10, 20, or 30.</param>
    /// <param name="Magnitude">Relative height of field's distortion. Suggested values: 0.05, .1.</param>
    /// <param name="Phase">Fraction of a complete cycle corresponding to an offset in the displacement from origin. Should be between 0 and 1.</param>
    public HeightField Adjust(DoubleBl Frequency, DoubleBl Magnitude, DoubleBl Phase) {
      return new HeightField(p => Magnitude * this[Frequency * (p + Phase)]);
    }



    /// <summary>
    /// Convert to normal map using approximation.
    /// </summary>
    /// <param name="Size">Size of target image undergoing approximation.</param>
    /// <param name="Scale">Extremeness of approximated normals.</param>
    /// <returns>Normal map populated with approximated values.</returns>
    public NormalMap ToNormals(Double2Bl Size, DoubleBl Scale) {
      return new NormalMap(UV => {
        var dx = this[new Double2Bl(UV.X + 1d / Size.X, UV.Y)] -
                 this[new Double2Bl(UV.X - 1d / Size.X, UV.Y)];
        var dy = this[new Double2Bl(UV.X, UV.Y + 1d / Size.Y)] -
                 this[new Double2Bl(UV.X, UV.Y - 1d / Size.Y)];
        return new Double3Bl(-dx * Scale, -dy * Scale, 1).Normalize;
      });
    }
  }

  public class ColorToColor : DoubleFuncBl<Tex2D,Double2Bl, ColorBl, ColorToColor> {
    public ColorToColor(Func<Tex2D, Double2Bl, ColorBl> F) : base(F) { }
    public ColorToColor(Func<ColorBl, ColorBl> F) : this((input,uv) => F(input[uv])) { }
    private ColorToColor(Func<Func<ColorToColor, ColorBl>, ColorBl> F) : base(F) { }
    public static ColorToColor MakeFunc(Func<Func<ColorToColor, ColorBl>, ColorBl> F) {
      return new ColorToColor(F);
    }

    /// <summary>
    /// Convert color based directly on another color.
    /// </summary>
    public ColorBl ForColor(ColorBl Color) {
      return Function(new Tex2D((Double2Bl uv) => Color), 0d);
    }
    /// <summary>
    /// Convert color based directly on a pixel addressed in a texture.
    /// </summary>
    public ColorBl ForPixel(Tex2D Input, Double2Bl UV) {
      return Function(Input, UV);
    }
    /*
    public ColorBl this[Tex2D Input, PointBl UV] {
      get { return Function(Input, UV); }
    }*/

    public static implicit operator ColorToColor(Func<Tex2D, Double2Bl, ColorBl> F) { return new ColorToColor(F); }
    public static implicit operator ColorToColor(Func<ColorBl, ColorBl> F) { return new ColorToColor(F); }
    public static implicit operator Func<ColorBl, ColorBl>(ColorToColor F) { return clr => F.ForColor(clr); }
  }

  /// <summary>
  /// A lighting function from a 3D position, normal, eye position to a color. 
  /// </summary>
  public class Lighting : DoubleFuncBl<Double3Bl, Double3Bl, Double3Bl, ColorToColor, Lighting> {
    /// <summary>
    /// Create a lighting function.
    /// </summary>
    /// <param name="F">Underlying function from a 3D position, normal, eye position to the Color of resulting light.</param>
    public Lighting(Func<Double3Bl, Double3Bl, Double3Bl, ColorToColor> F) : base(F) { }
    public Lighting(Func<Double3Bl, Double3Bl, Double3Bl, Func<ColorBl, ColorBl>> F) : this((p,n,e) => (ColorToColor)F(p, n, e)) { }
    public Lighting(Func<Double3Bl, Double3Bl, Double3Bl, Func<Tex2D, Double2Bl, ColorBl>> F) : this((p, n, e) => (ColorToColor)F(p, n, e)) { }

    public Lighting(Func<Double3Bl, Double3Bl, Func<ColorBl, ColorBl>> F) : this((p, n, e) => (ColorToColor)F(p, n)) { }
    public Lighting(Func<Double3Bl, Double3Bl, Func<Tex2D, Double2Bl, ColorBl>> F) : this((p, n, e) => (ColorToColor)F(p, n)) { }

    private Lighting(Func<Func<Lighting, ColorToColor>, ColorToColor> F) : base(F) { }
    public static Lighting MakeFunc(Func<Func<Lighting, ColorToColor>, ColorToColor> F) {
      return new Lighting(F);
    }

    public static implicit operator Lighting(Func<Double3Bl, Double3Bl, Double3Bl, ColorToColor> F) { return new Lighting(F); }
    public static implicit operator Lighting(Func<Double3Bl, Double3Bl, Double3Bl, Func<ColorBl,ColorBl>> F) { return new Lighting(F); }
    /// <summary>
    /// Apply lighting function to specified position and normal.
    /// </summary>
    public ColorToColor ApplyAt(Double3Bl Position, Double3Bl Normal) { return this[Position, Normal, new Double3Bl(0,0,-1)]; } 
    /// <summary>
    /// Apply lighting function to specified position, normal, and eye position.
    /// </summary>
    public ColorToColor ApplyAt(Double3Bl Position, Double3Bl Normal, Double3Bl Eye) { return this[Position, Normal, Eye]; }

    //public new ColorToColor this[Point3DBl Position, Point3DBl Normal, Point3DBl Eye] { get { return this[Position, Normal, Eye]; } }


    /// <summary>
    /// Create a texture from this lighting functiong using a specified surface and normal map.
    /// </summary>
    /// <param name="Surface">A PSurface or compatible function.</param>
    /// <param name="Normals">A NormalMap or compatible function.</param>
    public PixelEffect Apply(Surface Surface, NormalMap Normals) {
      return new PixelEffect(Input => new Tex2D(UV => this.ApplyAt(Surface[UV], Normals[UV]).ForPixel(Input, UV))); 
    }
    public PixelEffect Apply(Surface Surface, NormalMap Normals, Double3Bl Eye) {
      return new PixelEffect(Input => new Tex2D(UV => this[Surface[UV], Normals[UV], Eye].ForPixel(Input, UV))); 
    }


    /// <summary>
    /// Create a texture from specified surface.
    /// </summary>
    /// <param name="Surface">PSurface or compatible function.</param>
    public PixelEffect Apply(Surface Surface) {
        return this.Apply(Surface, (Surface).Normals);
    }
    /// <summary>
    /// Create a lit texture from specified surface.
    /// </summary>
    /// <param name="Surface">PSurface or compatible function.</param>
    /// <param name="Eye">Position of observer.</param>
    public PixelEffect Apply(Surface Surface, Double3Bl Eye) {
        return this.Apply(Surface, (Surface).Normals, Eye);
    }
    /// <summary>
    /// Create a texture from a specified height field and normal map with respect to a specific target texture size.
    /// </summary>
    /// <param name="Heights">HeightField or compatible function.</param>
    /// <param name="Normals">NormalMap or compatible function.</param>
    /// <param name="Size">Size.XY():Size of target texture that lighting will be applied to.
    /// Size.XY():Size of target texture that lighting will be applied to.
    /// Size.Z:Scale of the height to make it map the texture best.
    /// </param>
    public PixelEffect Apply(HeightField Heights, NormalMap Normals, Double3Bl Size) {
        return this.Apply(Heights, Normals, Size, new Double3Bl(0,0,0));
    }

    /// <summary>
    /// Create a texture from a specified height field and normal map with respect to a specific target texture size.
    /// Will apply parallax bias to resulting effect.
    /// </summary>
    /// <param name="Heights">HeightField or compatible function.</param>
    /// <param name="Normals">NormalMap or compatible function.</param>
    /// <param name="Size">Size.XY():Size of target texture that lighting will be applied to.
    /// Size.XY():Size of target texture that lighting will be applied to.
    /// Size.Z:Scale of the height to make it map the texture best.
    /// </param>
    /// <param name="Eye">the positon of eye/view point</param>
    /// <returns></returns>
    public PixelEffect Apply(HeightField Heights, NormalMap Normals, Double3Bl Size, Double3Bl Eye) {
      return new PixelEffect(input => new Tex2D(UV => {
        Double3Bl Position = new Double3Bl(UV * Size.XY(), Heights[UV] * Size.Z);
        var eyeDir = (Eye - Position).Normalize;
        var UV0 = UV + eyeDir.XY() * Heights[UV] * Size.Z;
        return this[Position, Normals[UV0], Eye].ForPixel(input, UV0);
      }));
    }
    /// <summary>
    /// Create a texture from a normal map with respect to a specific target texture size. Does not use high map like the previous API.
    /// </summary>
    /// <param name="Normals">NormalMap or compatible function.</param>
    /// <param name="Size">Size of target texture that lighting will be applied to.</param>
    public PixelEffect Apply(NormalMap Normals, Double2Bl Size) {
        // XXX: a property default eye?
        return this.Apply(Normals, Size, new Double3Bl(Size / 2, -1));
    }
    /// <summary>
    /// Create a texture from a normal map with respect to a specific target texture size. Does not use high map like the previous API.
    /// </summary>
    /// <param name="Normals">NormalMap or compatible function.</param>
    /// <param name="Size">Size of target texture that lighting will be applied to.</param>
    /// <param name="Eye">3D eye position of observer (default to middle of texture straight ahead)</param>
    public PixelEffect Apply(NormalMap Normals, Double2Bl Size, Double3Bl Eye) {
        return this.Apply(new HeightField(p => 0d), Normals, Size.AddZ(0), Eye);
    } 
    /// <summary>
    /// Create a texture from a specified height field with respect to a target texture size and approximate normal scale.
    /// </summary>
    /// <param name="Heights">HeightField or compatible function.</param>
    /// <param name="Size">Size of target texture that lighting will be applied to.</param>
    /// <param name="Scale">Scale to use in normal approximation.</param>
    public PixelEffect Apply(HeightField Heights, Double3Bl Size, DoubleBl Scale) {
        return this.Apply(Heights, (Heights).ToNormals(Size.XY(), Scale), Size, new Double3Bl(0,0,0));
    }
    /// <summary>
    /// Create a texture from a specified height field with respect to a target texture size and approximate normal scale.
    /// </summary>
    /// <param name="Heights">HeightField or compatible function.</param>
    /// <param name="Size">Size of target texture that lighting will be applied to.</param>
    /// <param name="Scale">Scale to use in normal approximation.</param>
    /// <param name="Eye">Location of observer.</param>
    public PixelEffect Apply(HeightField Heights, Double3Bl Size, DoubleBl Scale, Double3Bl Eye) {
        return this.Apply(Heights, (Heights).ToNormals(Size.XY(), Scale), Size, Eye);
    }
  }  
  public static class GraphicDefinitions {    
    public static readonly PCurve Circle = new PCurve(t => {
      return t.SinCosU.YX();
    });
    public static readonly Surface Sphere = new Surface((Double2Bl p) => {
      Double2Bl sin = p.SinU;
      Double2Bl cos = p.CosU;
      return new Double3Bl(sin[0] * cos[1], sin[0] * sin[1], cos[0]);
    });
    public static PCurve Bezier(params Double2Bl[] points) {
      return new PCurve(t => t.Bezier(points));
    }
    public static Surface Cylinder(DoubleBl Height) {
      // alternative definition.
      //return PCurve3DBl.Sweep(Circle.Function.AddZ(0d), t => new Point3DBl(0, 0, Height * t));


      return new Surface(p => {
        return new Double3Bl((p.X).PI2.SinCos.YX(), Height * p.Y);
      });
    }
    // height fields.
    public static readonly HeightField Flat = new HeightField(p => 0d);
    public static readonly HeightField Ripple = new HeightField(p => p.Length.SinU);
    public static readonly HeightField EggCrate = new HeightField((Double2Bl p) => {
      p = p.SinCosU;
      return p.X * p.Y;
    });
  }
  public static class GraphicsExtensions {
  }
}
